import cn.hutool.core.io.FileUtil;
import com.madgag.gif.fmsware.GifDecoder;
import org.opencv.core.*;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;

import static org.opencv.imgcodecs.Imgcodecs.IMREAD_GRAYSCALE;
import static org.opencv.imgcodecs.Imgcodecs.imread;
import static org.opencv.imgproc.Imgproc.*;

/**
 * OLED屏幕 Gif图取模
 * 生成c文件 https://blog.csdn.net/qq_37973192/article/details/125701279
 */
public class Main {
    static {
        System.load("E:\\DevWorkspace\\IDEA\\dev_plus\\gif-delivery\\src\\libs\\opencv_java3416.dll");
    }

    // 调整图片尺寸-宽
    private static final int WIDTH = 32;
    // 调整图片尺寸-高
    private static final int HEIGHT = 32;

    public static void main(String[] args) throws IOException {
        //感谢海螺张提供的gif,https://space.bilibili.com/2425374/dynamic?spm_id_from=444.42.list.card_avatar.click
        String inputPath = "E:\\DevWorkspace\\IDEA\\dev_plus\\gif-delivery\\src\\main\\resources\\img\\input\\254d78d446034865b05ccc9bdb19057e.gif";
        String outputPath = "E:\\DevWorkspace\\IDEA\\dev_plus\\gif-delivery\\src\\main\\resources\\img\\input\\outputGif\\output";
        int len = gifSeparate(inputPath, outputPath);
        List<Mat> matList = new ArrayList<>();
        for (int i = 0; i < len; i++) {
            Mat img = imread(outputPath + i + ".png", IMREAD_GRAYSCALE);
            Rect r = new Rect(8, 24, 216, 216);
            img = new Mat(img, r);
            resize(img, img, new Size(WIDTH, HEIGHT));
            //预处理
            Mat output = dealImg(img);
            threshold(output, output, 150, 255, THRESH_BINARY);
            matList.add(output);
//            imshow("img", output);
//            waitKey();
        }
        //将每一帧的二进制数据分8位存储
        List<List<String>> data = new ArrayList<>();
        for (Mat mat : matList) {
            List<String> strings = new ArrayList<>();
            StringBuilder builder = new StringBuilder();
            int length = 0;
            for (int i = 0; i < mat.rows(); i++) {
                for (int j = 0; j < mat.cols(); j++) {
                    if (mat.get(i, j)[0] == 255)
                        builder.append("0");
                    else
                        builder.append("1");
                    if (length == 7) {
                        length = -1;
                        strings.add(bin2hex(builder.toString()));
                        builder.delete(0, 8);
                    }
                    length++;
                }
            }
            data.add(strings);
        }
        String s = printData(data);
        //写入文件
        FileWriter writer = new FileWriter("Gif.h");
        writer.write(s);
        writer.close();
    }

    //格式化输出
    private static String printData(List<List<String>> data) {
        StringBuilder builder = new StringBuilder();
        builder.append("#ifndef GIF_H\n" +
                "#define GIF_H\n" +
                "\n");
        //一行最多十六个数据
        int maxLen = 16, len = 0;
        builder.append("static const int gif_length = " + data.size() + ";\n");
        builder.append("\nstatic const unsigned char gif[][" + data.get(0).size() + "] = {\n");
        for (List<String> datum : data) {
            for (String s : datum) {
                builder.append(s).append(", ");
                len++;
                if (len == maxLen) {
                    builder.append("\n");
                    len = 0;
                }
            }
        }
        builder.deleteCharAt(builder.length() - 1);
        builder.append("\n};\n\n");
        builder.append("#endif");
        return builder.toString();
    }

    //二进制倒序转化为十六进制
    private static String bin2hex(String bin) {
        StringBuilder string = new StringBuilder();
        for (int i = 0; i < bin.length(); i++) {
            string.append(bin.charAt(bin.length() - 1 - i));
        }
        int value = Integer.parseInt(string.toString(), 2);
        String result = "0x";
        if (Integer.toHexString(value).length() < 2)
            result += "0" + Integer.toHexString(value);
        else
            result += Integer.toHexString(value);
        return result;
    }

    //输入灰度图，返回将背景转化为白色的灰度图
    private static Mat dealImg(Mat input) {
        List<MatOfPoint> contours = new ArrayList<>();
        Mat mat = new Mat(input.size(), input.type());
        Mat result = new Mat(input.size(), input.type());
        //轮廓提取
        findContours(input, contours, mat, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
        Mat temp = new Mat(input.size(), input.type());
        input.copyTo(temp);
        input.copyTo(result);
        drawContours(temp, contours, -1, new Scalar(255), -1);
        for (int j = 0; j < input.rows(); j++) {
            for (int k = 0; k < input.cols(); k++) {
                //轮廓之外，背景设白
                if (temp.get(j, k)[0] == 0) {
                    result.put(j, k, 255.0);
                }
            }
        }
        return result;
    }

    //感谢：https://blog.csdn.net/qgqc_/article/details/105304397，这里稍稍修改了一下

    /**
     * @param originalSource 目标gif
     * @param newPath        分解后的文件夹路径
     * @return 分解后图片的数目
     */
    private static int gifSeparate(String originalSource, String newPath) throws IOException {
        GifDecoder gd = new GifDecoder();
        int status = gd.read(Files.newInputStream(new File(originalSource).toPath()));
        try {
            if (status != GifDecoder.STATUS_OK) {
                return -1;
            }
            for (int i = 0; i < gd.getFrameCount(); i++) {
                BufferedImage frame = gd.getFrame(i);
                ImageIO.write(frame, "png", new File(newPath + i + ".png"));
            }
            return gd.getFrameCount();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            for (int i = 0; i < gd.getFrameCount(); i++) {
                File file = new File(newPath + i + ".png");
                file.deleteOnExit();
            }
        }
        return -1;
    }
}
